﻿Imports System.Reflection

#Const PACKER_DEBUG = True

Namespace ExecutableImageOperations
    ''' <summary>
    ''' DLL Packer Coapplication
    ''' </summary>
    ''' <remarks>Application will end after Pack procedure returns</remarks>
    Module Packer
        ''' <summary>
        ''' Will pack all user Dlls required for this application to run, inside its Executable Assembly
        ''' </summary>
        ''' <remarks>Application will end when this procedure returns</remarks>
        Public Sub Pack()
            AddLog("   ** DLL PACKER ***")
            AddLog()

            If CurrentConfiguration.Standalone Then
                AddLog("FATAL: Assembly is ALREADY Standalone. Aborted.")
                Exit Sub
            End If

            Dim fiArphoon As IO.FileInfo

            ' SEARCH AND LOAD DLLS

            AddLog("Searching for ArpsEngine.dll...")
            fiArphoon = New IO.FileInfo(".\ArpsEngine.dll")
            If fiArphoon.Exists Then
                AddLogV("    Found! at " & fiArphoon.DirectoryName)
            Else
                AddLog(" CRITICAL FAILURE! ArpsEngine.dll was not found under this executable directory!")
                AddLog("  Aborted.")
                End
            End If

            AddLog("Loading ArpsEngine.dll...")
            Dim assemblyArphoon As Assembly = Assembly.ReflectionOnlyLoadFrom(fiArphoon.FullName)
            PrintAssemblyInformation(assemblyArphoon, fiArphoon)

            ' READ THIS ASSEMBLY INTO MEMORY
            AddLog("Reading this assembly into memory...")

            Dim outputAssembly As New IO.MemoryStream(32 * 1024 * 1024) ' 32 MiB initial buffer size
            Dim ArpsDllPosition As Integer
            Dim ArpsDllSize As Integer
            Dim ArpsMd5() As Byte
            Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
            Dim buffer() As Byte

            Dim fs As New IO.FileStream(System.Reflection.Assembly.GetExecutingAssembly().Location, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            Dim i As Integer
            For i = 1 To fs.Length
                outputAssembly.WriteByte(fs.ReadByte)
            Next

            fs.Close()
            fs.Dispose()
            fs = Nothing

            AddLog("Reading ArpsEngine.dll...")
            ArpsDllPosition = outputAssembly.Position
            fs = New IO.FileStream(fiArphoon.FullName, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
            ReDim buffer(fs.Length - 1)
            fs.Read(buffer, 0, buffer.Length)
            fs.Close()
            fs.Dispose()
            fs = Nothing

            ArpsDllSize = buffer.Length
            ArpsMd5 = md5.ComputeHash(buffer)
            outputAssembly.Write(buffer, 0, buffer.Length)
            Erase buffer

            AddLog("Closing assembly...")
            md5.Clear()
            md5 = Nothing
            outputAssembly.Write(BitConverter.GetBytes(ArpsDllPosition), 0, 4)
            outputAssembly.Write(BitConverter.GetBytes(ArpsDllSize), 0, 4)
            outputAssembly.Write(ArpsMd5, 0, 16)
            outputAssembly.Write(System.Text.Encoding.ASCII.GetBytes("PACKED"), 0, 6)

#If PACKER_DEBUG Then
            AddLog(" DEBUG: ArpsDllPosition=" & ArpsDllPosition)
            AddLog(" DEBUG:             bit=" & BitDump(ArpsDllPosition))
            AddLog(" DEBUG: ArpsDllSize=" & ArpsDllSize)
            AddLog(" DEBUG:         bit=" & BitDump(ArpsDllSize))
            Dim hash As String = ""
            For Each octect As Byte In ArpsMd5
                hash &= IIf(octect < 17, "0" & Hex(octect), Hex(octect))
            Next
            AddLog(" DEBUG: ArpsMd5=" & UCase(hash))
            AddLog(" DEBUG: TotalSize=" & outputAssembly.Position)
#End If

            Erase ArpsMd5

            AddLog("Saving new assembly...")
            buffer = outputAssembly.ToArray()
            outputAssembly.Close()
            outputAssembly.Dispose()
            outputAssembly = Nothing

            fs = New IO.FileStream(".\StandaloneAPC.exe", IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.None)
            fs.Write(buffer, 0, buffer.Length)
            AddLogV("   " & buffer.Length & " bytes saved.")
            Erase buffer
            fs.Close()
            fs.Dispose()
            fs = Nothing


            AddLog()
            AddLog(" PACKED SUCCESSFULLY")
        End Sub

        Private Sub PrintAssemblyInformation(ByRef target As Assembly, ByRef fi As IO.FileInfo)
            Dim assemblyName As AssemblyName = target.GetName()

            AddLogV("  Name: " & assemblyName.Name)
            AddLogV("  FullName: " & assemblyName.FullName)
            AddLogV("  Version: " & assemblyName.Version.ToString())
            AddLogV("  Culture: " & assemblyName.CultureInfo.DisplayName)
            AddLogV("  Architecture: " & assemblyName.ProcessorArchitecture.ToString())
            AddLogV("  Size: " & fi.Length & " bytes")
            AddLogV("  Created: " & fi.CreationTime.ToString(System.Globalization.CultureInfo.CurrentCulture))
            AddLogV("  LastModified: " & fi.LastWriteTime.ToString(System.Globalization.CultureInfo.CurrentCulture))

            assemblyName = Nothing
        End Sub


#If PACKER_DEBUG Then
        Private Function BitDump(ByVal value As Integer) As String
            Dim buffer() As Byte = BitConverter.GetBytes(value)

            Dim output As String = ""

            For Each b As Byte In buffer
                output &= IIf(b < 17, "0" & Hex(b), Hex(b)) & " "
            Next

            output = Trim(UCase(output))

            Return output
        End Function
#End If
    End Module
End Namespace